﻿#include "CalculationFormulaFunction.h"

#include <qmath.h>
#include <set>

namespace pst
{
    static QHash<QString, Func> g_symbolicOperationFunctionList = {
        {"+", Add},
        {"-", Subtract},
        {"*", Multiply},
        {"/", Divide},
        {"^", Power}};

    static QHash<QString, Func> g_formulaFunctionList = {
        {"average", Average},
        {"max", Max},
        {"min", Min},
        {"sqrt", Sqrt},
        {"exp", Exp},
        {"ln", Ln},
        {"abs", Abs},
        {"log", Log},
        {"sin", Sin},
        {"cos", Cos},
        {"tan", Tan},
        {"asin", ASin},
        {"acos", ACos},
        {"atan", ATan}};

    static QHash<QString, double> g_constantValueList = {
        {"pi", M_PI},
        {"eps0", 8.854187817e-12},
        {"mu0", 4 * M_PI * 1e-7},
        {"z0", 120 * M_PI},
        {"c0", 299792458}};

    Func GetSymbolicOperationFunc(const QString& funcName)
    {
        return g_symbolicOperationFunctionList.value(funcName);
    }

    QStringList GetFormulaFuncNames()
    {
        return g_formulaFunctionList.keys();
    }

    Func GetFormulaFunc(const QString& funcName)
    {
        return g_formulaFunctionList.value(funcName);
    }

    QStringList GetConstantNames()
    {
        return g_constantValueList.keys();
    }

    double GetConstanValue(const QString& constantName)
    {
        return g_constantValueList.value(constantName);
    }

    bool Add(const QVector<CalculationData>& value, CalculationData& result)
    {
        // value个数小于2，报错
        if (value.size() < 2)
        {
            return false;
        }
        auto resultTmp = value.first(); // 临时结果变量
        // 从第二个value开始加法计算
        for (int i = 1; i < value.size(); ++i)
        {
            CalculationData tmp;
            const auto& second = value.at(i);
            auto minMax = std::minmax(resultTmp.first.size(), second.first.size()); // 获取相加两个数据的数组个数范围
            // 相加两个数据中有一个无数据，报错
            if (minMax.first < 1)
            {
                return false;
            }
            // 相加两个数据中至少有一个为纯数据，没有key的限制
            if (resultTmp.second.isEmpty() || second.second.isEmpty())
            {
                // 第一个数据不为纯数据
                if (!resultTmp.second.isEmpty())
                {
                    // 第一个数据value与key个数不匹配，报错
                    if (resultTmp.first.size() != resultTmp.second.size())
                    {
                        return false;
                    }
                    tmp.second = resultTmp.second; // 赋值相加后的结果数据的key数据
                }
                // 第二个数据不为纯数据
                else if (!second.second.isEmpty())
                {
                    // 第二个数据value与key个数不匹配，报错
                    if (second.first.size() != second.second.size())
                    {
                        return false;
                    }
                    tmp.second = second.second; // 赋值相加后的结果数据的key数据
                }
                int maxIndex = 0;
                // 相加后的结果数据为纯数据
                if (tmp.second.isEmpty())
                {
                    maxIndex = minMax.second; // 最大范围为和最大个数
                }
                else
                {
                    maxIndex = tmp.second.size(); // key数据个数为和最大个数
                }
                tmp.first.reserve(maxIndex); // 开辟和的value内存
                // 计算和的value值
                for (int i = 0; i < maxIndex; ++i)
                {
                    // 相加两个数据中有一个为单数值的1+多的情况，单数值与另一个数组依次相加
                    if (minMax.first == 1)
                    {
                        double firstNumber = resultTmp.first.at(resultTmp.first.size() == 1 ? 0 : i);
                        double secondNumber = second.first.at(second.first.size() == 1 ? 0 : i);
                        tmp.first.append(firstNumber + secondNumber);
                    }
                    // 都是数组
                    else
                    {
                        // 共有范围内直接相加
                        if (i < minMax.first)
                        {
                            tmp.first.append(resultTmp.first.at(i) + second.first.at(i));
                        }
                        // 第一个数据超出直接获取第一个数据的值
                        else if (resultTmp.first.size() > i)
                        {
                            tmp.first.append(resultTmp.first.at(i));
                        }
                        // 第二个数据超出直接获取第二个数据的值
                        else if (second.first.size() > i)
                        {
                            tmp.first.append(second.first.at(i));
                        }
                    }
                }
            }
            else
            {
                // 都有key数据的相加数组大小不同，报错
                if (resultTmp.first.size() != resultTmp.second.size() || second.first.size() != second.second.size())
                {
                    return false;
                }
                QVector<double> keyData;
                keyData.append(resultTmp.second);
                keyData.append(second.second);
                std::set set(keyData.begin(), keyData.end());                                  // 保持数据顺序去重
                keyData = QVector<double>::fromStdVector(std::vector(set.begin(), set.end())); // 两个数据key数据的合集
                tmp.second = keyData;
                tmp.first.reserve(keyData.size());
                for (double key : keyData)
                {
                    int firstIndex = resultTmp.second.indexOf(key); // 第一个数据查找对应key的索引
                    int secondIndex = second.second.indexOf(key);   // 第二个数据查找对应key的索引
                    // 两个数据都找到对应的value值
                    if (firstIndex > -1 && secondIndex > -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex) + second.first.at(secondIndex));
                    }
                    // 第一个数据找到，第二个数据不存在该key
                    else if (firstIndex > -1 && secondIndex == -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex));
                    }
                    // 第一个数据不存在该key，第二个人数据找到
                    else if (firstIndex == -1 && secondIndex > -1)
                    {
                        tmp.first.append(second.first.at(secondIndex));
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            resultTmp = std::move(tmp); // 存放到临时结果中
        }
        result = std::move(resultTmp); // 返回计算结果数据
        return true;
    }

    // 整体处理同加法
    bool Subtract(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() < 2)
        {
            return false;
        }
        auto resultTmp = value.first();
        for (int i = 1; i < value.size(); ++i)
        {
            CalculationData tmp;
            const auto& second = value.at(i);
            auto minMax = std::minmax(resultTmp.first.size(), second.first.size());
            if (minMax.first < 1)
            {
                return false;
            }
            if (resultTmp.second.isEmpty() || second.second.isEmpty())
            {
                if (!resultTmp.second.isEmpty())
                {
                    if (resultTmp.first.size() != resultTmp.second.size())
                    {
                        return false;
                    }
                    tmp.second = resultTmp.second;
                }
                else if (!second.second.isEmpty())
                {
                    if (second.first.size() != second.second.size())
                    {
                        return false;
                    }
                    tmp.second = second.second;
                }
                int maxIndex = 0;
                if (tmp.second.isEmpty())
                {
                    maxIndex = minMax.second;
                }
                else
                {
                    maxIndex = tmp.second.size();
                }
                tmp.first.reserve(maxIndex);
                for (int i = 0; i < maxIndex; ++i)
                {
                    if (minMax.first == 1)
                    {
                        double firstNumber = resultTmp.first.at(resultTmp.first.size() == 1 ? 0 : i);
                        double secondNumber = second.first.at(second.first.size() == 1 ? 0 : i);
                        tmp.first.append(firstNumber - secondNumber);
                    }
                    else
                    {
                        if (i < minMax.first)
                        {
                            tmp.first.append(resultTmp.first.at(i) - second.first.at(i));
                        }
                        else if (resultTmp.first.size() > i)
                        {
                            tmp.first.append(resultTmp.first.at(i));
                        }
                        else if (second.first.size() > i)
                        {
                            tmp.first.append(second.first.at(i));
                        }
                    }
                }
            }
            else
            {
                if (resultTmp.first.size() != resultTmp.second.size() || second.first.size() != second.second.size())
                {
                    return false;
                }
                QVector<double> keyData;
                keyData.append(resultTmp.second);
                keyData.append(second.second);
                std::set set(keyData.begin(), keyData.end()); // 保持数据顺序去重
                keyData = QVector<double>::fromStdVector(std::vector(set.begin(), set.end()));
                tmp.second = keyData;
                tmp.first.reserve(keyData.size());
                for (double key : keyData)
                {
                    int firstIndex = resultTmp.second.indexOf(key);
                    int secondIndex = second.second.indexOf(key);
                    if (firstIndex > -1 && secondIndex > -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex) - second.first.at(secondIndex));
                    }
                    else if (firstIndex > -1 && secondIndex == -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex));
                    }
                    else if (firstIndex == -1 && secondIndex > -1)
                    {
                        tmp.first.append(second.first.at(secondIndex));
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            resultTmp = std::move(tmp);
        }
        result = std::move(resultTmp);
        return true;
    }

    // 整体处理同加法
    bool Multiply(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() < 2)
        {
            return false;
        }
        auto resultTmp = value.first();
        for (int i = 1; i < value.size(); ++i)
        {
            CalculationData tmp;
            const auto& second = value.at(i);
            auto minMax = std::minmax(resultTmp.first.size(), second.first.size());
            if (minMax.first < 1)
            {
                return false;
            }
            if (resultTmp.second.isEmpty() || second.second.isEmpty())
            {
                if (!resultTmp.second.isEmpty())
                {
                    if (resultTmp.first.size() != resultTmp.second.size())
                    {
                        return false;
                    }
                    tmp.second = resultTmp.second;
                }
                else if (!second.second.isEmpty())
                {
                    if (second.first.size() != second.second.size())
                    {
                        return false;
                    }
                    tmp.second = second.second;
                }
                int maxIndex = 0;
                if (tmp.second.isEmpty())
                {
                    maxIndex = minMax.second;
                }
                else
                {
                    maxIndex = tmp.second.size();
                }
                tmp.first.reserve(maxIndex);
                for (int i = 0; i < maxIndex; ++i)
                {
                    if (minMax.first == 1)
                    {
                        double firstNumber = resultTmp.first.at(resultTmp.first.size() == 1 ? 0 : i);
                        double secondNumber = second.first.at(second.first.size() == 1 ? 0 : i);
                        tmp.first.append(firstNumber * secondNumber);
                    }
                    else
                    {
                        if (i < minMax.first)
                        {
                            tmp.first.append(resultTmp.first.at(i) * second.first.at(i));
                        }
                        else if (resultTmp.first.size() > i)
                        {
                            tmp.first.append(resultTmp.first.at(i));
                        }
                        else if (second.first.size() > i)
                        {
                            tmp.first.append(second.first.at(i));
                        }
                    }
                }
            }
            else
            {
                if (resultTmp.first.size() != resultTmp.second.size() || second.first.size() != second.second.size())
                {
                    return false;
                }
                QVector<double> keyData;
                keyData.append(resultTmp.second);
                keyData.append(second.second);
                std::set set(keyData.begin(), keyData.end()); // 保持数据顺序去重
                keyData = QVector<double>::fromStdVector(std::vector(set.begin(), set.end()));
                tmp.second = keyData;
                tmp.first.reserve(keyData.size());
                for (double key : keyData)
                {
                    int firstIndex = resultTmp.second.indexOf(key);
                    int secondIndex = second.second.indexOf(key);
                    if (firstIndex > -1 && secondIndex > -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex) * second.first.at(secondIndex));
                    }
                    else if (firstIndex > -1 && secondIndex == -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex));
                    }
                    else if (firstIndex == -1 && secondIndex > -1)
                    {
                        tmp.first.append(second.first.at(secondIndex));
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            resultTmp = std::move(tmp);
        }
        result = std::move(resultTmp);
        return true;
    }

    // 整体处理同加法
    bool Divide(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() < 2)
        {
            return false;
        }
        auto resultTmp = value.first();
        for (int i = 1; i < value.size(); ++i)
        {
            CalculationData tmp;
            const auto& second = value.at(i);
            auto minMax = std::minmax(resultTmp.first.size(), second.first.size());
            if (minMax.first < 1)
            {
                return false;
            }
            if (resultTmp.second.isEmpty() || second.second.isEmpty())
            {
                if (!resultTmp.second.isEmpty())
                {
                    if (resultTmp.first.size() != resultTmp.second.size())
                    {
                        return false;
                    }
                    tmp.second = resultTmp.second;
                }
                else if (!second.second.isEmpty())
                {
                    if (second.first.size() != second.second.size())
                    {
                        return false;
                    }
                    tmp.second = second.second;
                }
                int maxIndex = 0;
                if (tmp.second.isEmpty())
                {
                    maxIndex = minMax.second;
                }
                else
                {
                    maxIndex = tmp.second.size();
                }
                tmp.first.reserve(maxIndex);
                for (int i = 0; i < maxIndex; ++i)
                {
                    if (minMax.first == 1)
                    {
                        double firstNumber = resultTmp.first.at(resultTmp.first.size() == 1 ? 0 : i);
                        double secondNumber = second.first.at(second.first.size() == 1 ? 0 : i);
                        // 被除数为0，报错
                        if (secondNumber == 0)
                        {
                            return false;
                        }
                        tmp.first.append(firstNumber / secondNumber);
                    }
                    else
                    {
                        if (i < minMax.first)
                        {
                            // 被除数为0，报错
                            if (second.first.at(i) == 0)
                            {
                                return false;
                            }
                            tmp.first.append(resultTmp.first.at(i) / second.first.at(i));
                        }
                        else if (resultTmp.first.size() > i)
                        {
                            tmp.first.append(resultTmp.first.at(i));
                        }
                        else if (second.first.size() > i)
                        {
                            tmp.first.append(second.first.at(i));
                        }
                    }
                }
            }
            else
            {
                if (resultTmp.first.size() != resultTmp.second.size() || second.first.size() != second.second.size())
                {
                    return false;
                }
                QVector<double> keyData;
                keyData.append(resultTmp.second);
                keyData.append(second.second);
                std::set set(keyData.begin(), keyData.end()); // 保持数据顺序去重
                keyData = QVector<double>::fromStdVector(std::vector(set.begin(), set.end()));
                tmp.second = keyData;
                tmp.first.reserve(keyData.size());
                for (double key : keyData)
                {
                    int firstIndex = resultTmp.second.indexOf(key);
                    int secondIndex = second.second.indexOf(key);
                    if (firstIndex > -1 && secondIndex > -1)
                    {
                        // 被除数为0，报错
                        if (second.first.at(secondIndex) == 0)
                        {
                            return false;
                        }
                        tmp.first.append(resultTmp.first.at(firstIndex) / second.first.at(secondIndex));
                    }
                    else if (firstIndex > -1 && secondIndex == -1)
                    {
                        tmp.first.append(resultTmp.first.at(firstIndex));
                    }
                    else if (firstIndex == -1 && secondIndex > -1)
                    {
                        tmp.first.append(second.first.at(secondIndex));
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            resultTmp = std::move(tmp);
        }
        result = std::move(resultTmp);
        return true;
    }

    bool Power(const QVector<CalculationData>& value, CalculationData& result)
    {
        // value个数小于2，报错
        if (value.size() < 2)
        {
            return false;
        }
        auto resultTmp = value.first();
        for (int i = 1; i < value.size(); ++i)
        {
            CalculationData tmp;
            const auto& second = value.at(i);
            // 指数不为单数值的纯数据类容，报错
            if (second.first.size() != 1 || !second.second.isEmpty())
            {
                return false;
            }
            auto minMax = std::minmax(resultTmp.first.size(), second.first.size());
            if (minMax.first < 1)
            {
                return false;
            }
            if (resultTmp.first.size() != resultTmp.second.size())
            {
                return false;
            }
            tmp.second = resultTmp.second;
            tmp.first.reserve(minMax.second);
            for (int i = 0; i < minMax.second; ++i)
            {
                tmp.first.append(std::pow(resultTmp.first.at(i), second.first.at(0)));
            }
            resultTmp = std::move(tmp);
        }
        result = std::move(resultTmp);
        return true;
    }

    bool Average(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.isEmpty())
        {
            return false;
        }
        double sum = 0;
        int number = 0;
        for (const auto& vTmp : value)
        {
            const auto& v = vTmp.first;
            sum = std::accumulate(v.begin(), v.end(), sum);
            number += v.size();
        }
        if (number == 0)
        {
            return false;
        }
        result = CalculationData({sum / number}, {});
        return true;
    }

    bool Max(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.isEmpty())
        {
            return false;
        }
        QVector<double> tmp;
        for (const auto& vTmp : value)
        {
            const auto& v = vTmp.first;
            if (!v.isEmpty())
            {
                tmp.append(*std::max_element(v.begin(), v.end()));
            }
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result = CalculationData({*std::max_element(tmp.begin(), tmp.end())}, {});
        return true;
    }

    bool Min(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.isEmpty())
        {
            return false;
        }
        QVector<double> tmp;
        for (const auto& vTmp : value)
        {
            const auto& v = vTmp.first;
            if (!v.isEmpty())
            {
                tmp.append(*std::min_element(v.begin(), v.end()));
            }
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result = CalculationData({*std::min_element(tmp.begin(), tmp.end())}, {});
        return true;
    }

    bool Sqrt(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(sqrt(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Exp(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(exp(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Ln(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(log(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Abs(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(abs(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Log(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(log10(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Sin(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(sin(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Cos(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(cos(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool Tan(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(tan(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool ASin(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(asin(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool ACos(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(acos(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }

    bool ATan(const QVector<CalculationData>& value, CalculationData& result)
    {
        if (value.size() != 1)
        {
            return false;
        }
        QVector<double> tmp;
        tmp.reserve(value.first().first.size());
        for (double number : value.first().first)
        {
            tmp.append(atan(number));
        }
        if (tmp.isEmpty())
        {
            return false;
        }
        result.first = std::move(tmp);
        result.second = value.first().second;
        return true;
    }
} // namespace pst
